﻿// INGENIOUS FRAMEWORK - Semantic Web Development Simplified
// Copyright (C) 2012 Linked Data Tools

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

using Microsoft.Synchronization;
using Microsoft.Synchronization.MetadataStorage;

using Ingenious.Graph;

// - Note: Having a synchronization provider structure such as this means that external applications
// can attach to the target data store and update it, and we can reflect those changes in Ingenious.

// - It also means we can work with storage silos that do not support change notification, by providing
// a sync metadata framework to intelligently compare snapshots and re-sync.

// - Data converters could then also be integrated into this framework and even be two-way rather than
// one-way. Is this possible? Or desirable?

namespace Ingenious.Core
{
    /// <summary>
    /// Provides synchronization capabilities between an Ingenious triple model and other
    /// data stores that have appropriate synchronization providers
    /// </summary>
    public class IngeniousSyncProvider<TObjectId> : KnowledgeSyncProvider, IChangeDataRetriever, INotifyingChangeApplierTarget
    {
        private SqlMetadataStore m_metadataStore;
        private ReplicaMetadata m_replicaMetadata;

        private readonly GraphProxy<TObjectId> m_graphProxy;
        private readonly string m_replicaMetadataFile;

        private SyncId m_replicaId;
        private SyncIdFormatGroup m_syncIdFormatGroup;

        #region Object Lifetime
        public IngeniousSyncProvider(GraphProxy<TObjectId> graphProxy, string replicaMetadataFile)
        {
            if (graphProxy == null)
            {
                throw new NullReferenceException("graphProxy");
            }

            if (replicaMetadataFile == null)
            {
                throw new NullReferenceException("metadataStoreFileName");
            }

            m_graphProxy = graphProxy;
            m_replicaMetadataFile = replicaMetadataFile;          
        }
        #endregion

        #region Change Metadata Store Management
        private void InitialiseMetadataStore()
        {
            // Values for adding a custom field to the metadata store
            List<FieldSchema> fields = new List<FieldSchema>();

            // Create or open the metadata store, initializing it with the id formats
            // we'll use to reference our items and endpoints
            if (!File.Exists(m_replicaMetadataFile))
            {               
                m_metadataStore = SqlMetadataStore.CreateStore(m_replicaMetadataFile);
                m_replicaMetadata = m_metadataStore.InitializeReplicaMetadata(m_syncIdFormatGroup, new SyncId(Guid.NewGuid()), fields, null/*No indexes to create*/);
            }
            else
            {
                m_metadataStore = SqlMetadataStore.OpenStore(m_replicaMetadataFile);
                m_replicaMetadata = m_metadataStore.GetSingleReplicaMetadata();
            }

            m_replicaId = m_replicaMetadata.ReplicaId;
        }

        private void CloseMetadataStore()
        {
            m_metadataStore.Dispose();
            m_metadataStore = null;
        }
        #endregion

        public override void BeginSession(SyncProviderPosition position, SyncSessionContext syncSessionContext)
        {
            Debug.WriteLine("BeginSession");


        }

        public override void EndSession(SyncSessionContext syncSessionContext)
        {
            Debug.WriteLine("EndSession");
        }

        public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge, out object changeDataRetriever)
        {
            Debug.WriteLine("GetChangeBatch");
            changeDataRetriever = null;

            return null;
        }

        public override FullEnumerationChangeBatch GetFullEnumerationChangeBatch(uint batchSize, SyncId lowerEnumerationBound, SyncKnowledge knowledgeForDataRetrieval, out object changeDataRetriever)
        {
            Debug.WriteLine("GetFullEnumerationChangeBatch");
            changeDataRetriever = null;

            return null;
        }

        public override void GetSyncBatchParameters(out uint batchSize, out SyncKnowledge knowledge)
        {
            Debug.WriteLine("GetSyncBatchParameters");

            batchSize = 0;
            knowledge = null;
        }

        public override SyncIdFormatGroup IdFormats
        {
            get
            {
                Debug.WriteLine("get_IdFormats");
                return null;
            }
        }

        public override void ProcessChangeBatch(ConflictResolutionPolicy resolutionPolicy, ChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
        {
            Debug.WriteLine("ProcessChangeBatch");
        }

        public override void ProcessFullEnumerationChangeBatch(ConflictResolutionPolicy resolutionPolicy, FullEnumerationChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
        {
            Debug.WriteLine("ProcessFullEnumerationChangeBatch");
        }
        
        public object LoadChangeData(LoadChangeContext loadChangeContext)
        {
            throw new NotImplementedException();
        }

        public IChangeDataRetriever GetDataRetriever()
        {
            throw new NotImplementedException();
        }

        public ulong GetNextTickCount()
        {
            throw new NotImplementedException();
        }

        public void SaveChangeWithChangeUnits(ItemChange change, SaveChangeWithChangeUnitsContext context)
        {
            throw new NotImplementedException();
        }

        public void SaveConflict(ItemChange conflictingChange, object conflictingChangeData, SyncKnowledge conflictingChangeKnowledge)
        {
            throw new NotImplementedException();
        }

        public void SaveItemChange(SaveChangeAction saveChangeAction, ItemChange change, SaveChangeContext context)
        {
            throw new NotImplementedException();
        }

        public void StoreKnowledgeForScope(SyncKnowledge knowledge, ForgottenKnowledge forgottenKnowledge)
        {
            throw new NotImplementedException();
        }

        public bool TryGetDestinationVersion(ItemChange sourceChange, out ItemChange destinationVersion)
        {
            throw new NotImplementedException();
        }

        #region Properties
        public SyncId ReplicaId
        {
            get
            {
                if (m_replicaId == null)
                {
                    InitialiseMetadataStore();
                    m_replicaId = m_replicaMetadata.ReplicaId;
                    CloseMetadataStore();
                }

                return m_replicaId;
            }
        }
        #endregion
    }
}
